/*
Copyright (c) 1997 (or so), 2012 David Raffensperger

Permission is hereby granted, free of charge, to any person obtaining a copy 
of this software and associated documentation files (the "Software"), to deal 
in the Software without restriction, including without limitation the rights 
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
copies of the Software, and to permit persons to whom the Software is 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all 
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
SOFTWARE.
*/

/*****************************************************************************
*		                      Battle                                         *
*                                                                            *
*                is a game made by David A. Raffensperger                    *
*                                                                            *
*  In Battle you use the W, S, A, and D keys to move and the Space Bar to    *
* to shoot. use T and H to rotate your ship. But avoid the feard monsters!   *
*                                                                            *
*****************************************************************************/

// For Sleep
#include "windows.h" 

#include "graph_win.h"
#include <stdlib.h>
#include <conio.h>
#include <time.h>

struct Enemy
{
    int Col;
    int Row;
    int Dir;
    int Died;
    int Armor;
};

enum NOTES      /* Enumeration of notes and frequencies     */
{
    C0 = 262, D0 = 296, E0 = 330, F0 = 349, G0 = 392, A0 = 440, B0 = 494,
    C1 = 523, D1 = 587, E1 = 659, F1 = 698, G1 = 784, A1 = 880, B1 = 988,
    EIGHTH = 125, QUARTER = 250, HALF = 500, WHOLE = 1000, END = 0
};

//#define FALSE 0
//#define TRUE !FALSE
#define MAX_ENEMY_ARMOR 2
#define SPACE_BAR 32
#define ENTER 13
#define ESC 27
#define F1 0+59
#define UP_ARROW 0+72
#define DOWN_ARROW 0+80
#define RIGHT_ARROW 0+77
#define LEFT_ARROW 0+75
#define NO_KEY -1
#define UNDERLINE 0x0707
#define FULL_BLOCK_CURSOR 0x0007
#define DOUBLE_CURSOR 0x0607
#define NO_CURSOR 0x2000
#define RIGHT_COL_LIMIT 80
#define LEFT_COL_LIMIT 1
#define TOP_ROW_LIMIT 2
#define BOTTOM_ROW_LIMIT 25

const char *SHIP[4] = { "^", ">", "v", "<" };
const int ENEMY_COLOR[MAX_ENEMY_ARMOR + 1] = {7, 1, 4};

void Pause ( float delayNum );
int GetKey ( void );
void Shoot ();
void Track ( int i, struct Enemy *enemyStatus, int youRow, int youCol );
void Help ( void );
void SpecialHelp2 ( void );
void specialHelp1 ( void );
void GetInput ( int *Diff, int *youRow, int *youCol, int *Shields );
void Quit ( void );
void Kill ( struct Enemy *DeadMan, int *EnemiesAlive );

//void Beep( int frequency, int duration );
//void Sleep( clock_t wait );

/* Declarations: Global variables to hold game state */
char *whatKeyWas, kbd, *speedString, *delayNum, *StillWantsToPlay = "y",
	TrashString[256];
int timeleft = 1000, level = 1, youFacing = 1, enemiesAlive = 6,
	wantsToQuit = FALSE, i, g, q, Shields = 0, pointsGiven = 0, Score = 0, youRow = 0,
	youCol = 0, speed = 0, Diff = 0, count = 0, count1 = 0;
struct Enemy enemyStatus [ 25 ];	

// Helper functions to break up the main loop.
void checkWinLoseConditions();
void moveEnemies();
void printGameState();
void processGameKeyPressed();

void main ( void )
{
	_setvideomode ( _TEXTC80 );
	_settextcursor ( NO_CURSOR );
	/*RANDOMIZE TIMER*/
	/*VIEW _outtext ( 1 TO 25*/

	for ( i = 1/*2*/; i <= 5; i++ )
	{
		enemyStatus[i].Died = FALSE;
		enemyStatus[i].Row = i;
		enemyStatus[i].Dir = 1;
		enemyStatus[i].Col = i;
		enemyStatus[i].Armor = MAX_ENEMY_ARMOR;
	}

	/*enemyStatus(1).Row = 6
	enemyStatus(1).Col = 6
	enemyStatus(1).Dir = 1
	enemyStatus(1).Armor = 5*/

   GetInput ( &Diff, &youRow, &youCol, &Shields );

	/*ON TIMER(1) GOSUB lessPoints
	TIMER ON*/

	while ( TRUE )
	{
		if ( pointsGiven > 49 )
			pointsGiven = 49;
		if ( timeleft < 0 )
			timeleft = 0;							

		_settextposition ( youRow, youCol );
		_outtext ( " " );

		processGameKeyPressed();

		for ( i = 1; i <= level * 5; i++ )
		{
			_settextposition ( enemyStatus[i].Row, enemyStatus[i].Col );
			_outtext ( " " );
		}

		if (rand() % (2 * (speed + 1)) == 0) {
			moveEnemies();
		}

		printGameState();

		Sleep(100);

		checkWinLoseConditions();
    	 /* TRUE */

		/*		
			
		// Clear old locations of enemies and of you
		switch  ( enemyStatus[i].Dir )
		{
		case 1 :
			_settextposition ( enemyStatus[i].Row + 1, enemyStatus[i].Col );
			_outtext ( " " );
			break;
		case 2 :
			_settextposition ( enemyStatus[i].Row - 1, enemyStatus[i].Col );
			_outtext ( " " );
      	break;
		case 3 :
			_settextposition ( enemyStatus[i].Row, enemyStatus[i].Col + 1 );
			_outtext ( " " );
      	break;
		case 4 :
			_settextposition ( enemyStatus[i].Row, enemyStatus[i].Col - 1 );
			_outtext ( " " );
      	break;
		default :
			;
		}
		
		*/    	
	}
}

void processGameKeyPressed() 
{
	if ( kbhit ( ) )
	{
		switch ( getch ( ) )
		{
		case UP_ARROW :
			youRow--;
			if ( youRow <= 2 )
				youRow = 24;
			break;
		case DOWN_ARROW :
			youRow++;
			if ( youRow >= 25 )
				youRow = 3;
        break;
		case LEFT_ARROW :
			youCol--;
			if ( youCol <= 1 )
				youCol = 79;
        break;
		case RIGHT_ARROW :
			youCol++;
			if ( youCol >= 80 )
				youCol = 2;
        break;
		case 116 :	/* t */
		case 84 :   /* T */
			youFacing++;
			if ( youFacing > 3 )
				youFacing = 0;
			break;
		/* case 72 : */ /* error "case value '72' already used"  */
		/* h */
		case 104 :  /* H */
			youFacing--;
			if ( youFacing < 0 )
				youFacing = 3;
			break;
		case ' ' :
   			Shoot();
		 	break;
		case F1 :
			Help ( );
			break;
		case ESC :
			Quit ( );
			break;
		default :
			;
	    }
	}
}

void printGameState() 
{
	for ( g = 1; g <= Shields; g++ ) {
		_settextcolor ( 2 );
		_settextposition ( 1, 80 - g );
		_outtext ( "S" );
	}

 	_settextcolor ( 3 );
	_settextposition ( 1, 28 );
	_outtext ( "Battle by David" );
	_settextcolor ( 9 );
	_settextposition ( 1, 1 );
		
	_settextcolor ( 7 );
	_settextposition ( 1, 50 );
	_outtext ( "Press F1 for help" );
	_settextcolor ( 10 );
    _settextposition ( 1, 12 );
    _outtext ( itoa ( Score, TrashString, 10 ) );

	_settextcolor ( 10 );
	_settextposition ( youRow, youCol );
	_outtext ( SHIP [ youFacing ] );	

	for ( i = 1; i <= level * 5; i++ )
	{
		if ( !enemyStatus[i].Died )
		{
			_settextcolor ( ENEMY_COLOR [ enemyStatus[i].Armor ] );
			_settextposition ( enemyStatus[i].Row, enemyStatus[i].Col );			
			_outtext ( "X" );
		}
	}
}

void moveEnemies()
{
	for ( i = 1; i <= level * 5; i++ )
	{
		if (enemyStatus[i].Died) {
			continue;
		}

		switch ( enemyStatus[i].Dir )
		{
		case 1 :
			enemyStatus[i].Row--;
			if ( enemyStatus[i].Col <= 2 )
				enemyStatus[i].Col = 79;
			break;
		case 2 :
			enemyStatus[i].Row++;
			if ( enemyStatus[i].Col >= 80 )
				enemyStatus[i].Col = 3;
			break;
		case 3 :
			enemyStatus[i].Col--;
			if ( enemyStatus[i].Row <= 1 )
				enemyStatus[i].Row = 24;
        break;
		case 4 :
			enemyStatus[i].Col++;
			if ( enemyStatus[i].Row >= 25 )
				enemyStatus[i].Row = 2;
        break;
		default :
			;
      	}

		Track(i, enemyStatus, youRow, youCol);			

		if ( enemyStatus[i].Col <= 2 )
			enemyStatus[i].Col = 79;
		if ( enemyStatus[i].Col >= 80 )
			enemyStatus[i].Col = 3;
		if ( enemyStatus[i].Row >= 25 )
			enemyStatus[i].Row = 2;
		if ( enemyStatus[i].Row <= 1 )
			enemyStatus[i].Row = 24;			
			
		if ( enemyStatus[i].Row == youRow && enemyStatus[i].Col == youCol )
		{
			Shields--;
			enemyStatus[i].Row = 12;
			enemyStatus[i].Col = 20;
			enemyStatus[i].Dir = 1;
      		_settextcolor ( 20 );
      		_settextposition ( 3, 15 );
      		_outtext ( "You have been bit by an enemy, press any key to continue" );
      		getch ( );
      		_clearscreen ( _GCLEARSCREEN );
      		for ( count = 1; count <= 29; count++ )
			{
      			Beep ( 3000 - count * 100, 1 );
      		}

      		switch ( Diff )
			{
      		case 1 :
       			youCol = 78;
         		youRow = 23;
					break;
      		case 2 :
         		youCol = 60;
         		youRow = 18;
         		break;
      		case 3 :
         		youCol = 40;
         		youRow = 12;
         		break;
				default :
					;
      		}
		}
	}
}

void checkWinLoseConditions()
{
	if ( Shields <= 0 )
	{
      	_clearscreen ( _GCLEARSCREEN );
      	_settextcolor ( 4 );
      	_settextposition ( 12, 30 );
      	_outtext ( "You have died!" );
      	getch ( );
		
		_settextcolor ( 0 );
   		exit ( FALSE );
    }

    if ( enemiesAlive <= 1 )
	{
      	_clearscreen ( _GCLEARSCREEN );
      	_settextcolor ( 2 );
      	_settextposition ( 10, 30 );
      	_outtext( "Great job you won!");      	

		Sleep(2000);
		
		_settextcolor ( 0 );
      	exit ( 0 );      	
    }
}

void Kill ( struct Enemy *DeadMan, int *EnemiesAlive )
{
 	DeadMan -> Died = TRUE;
	DeadMan -> Dir = 0;

	*EnemiesAlive--;
}

void Quit ( )
{
	/*TODO : Add choice*/
	exit ( 0 );
}

void GetInput ( int *Diff, int *youRow, int *youCol, int *Shields )
{
	int speedString = 0, speed = 0, done = FALSE, kbd = 0, selected = 0;

	_clearscreen ( _GCLEARSCREEN );
	_settextcolor ( 7 );
	_settextposition ( 1, 50 );
	_outtext ( "Press F1 for help" );
	_settextcolor ( 3 );
	_settextposition ( 1, 30 );
	_outtext ( "Battle by David" );

	_settextcolor ( 7 );
	_settextposition ( 3, 20 );
	_outtext ( "Type in a number between 0 and 9," );
	_settextposition ( 4, 17 );
	_outtext ( "for how fast do you want the game to go" );

	_settextcolor ( 9 );

	while ( 1 )
	{
  		_settextposition ( 1, 1 );
		/*_outtext ( TIME$*/
		speedString = GetKey ( );
		if ( speedString == F1 )
			specialHelp1 ( );
		if ( speedString != F1 && speedString != NO_KEY )
			break;
	}

	//speed = speedString - 48;
	speed = speedString;

	_settextcolor ( 7 );
	_settextposition ( 3, 1 );
	/*          123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
	_outtext ( "                                                                                " );
	/* 80 spaces to clear the line */

	_settextposition ( 4, 1 );
	/*          123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 */
	_outtext ( "                                                                                ");
	/* 80 spaces to clear the line */

	_settextposition ( 3, 25 );
	_outtext ( "You want the game to be:" );

	while ( done != TRUE )
	{
		kbd = GetKey ( );

		_settextcolor ( 9 );
		_settextposition ( 1, 1 );
		/*_outtext ( TIME$*/

		switch ( kbd )
		{
		case ENTER:
			done = TRUE;
			break;
		case UP_ARROW :
			selected++;
			break;
		case DOWN_ARROW :
			selected--;
			break;
		case RIGHT_ARROW :
			selected++;
			break;
		case LEFT_ARROW :
			selected--;
			break;
		case SPACE_BAR :
			selected++;
			break;
		case F1 :
			SpecialHelp2 ( );
			break;
		default :
			;
		}

		_settextcolor ( 7 );

  		switch ( selected )
		{
    	case 1 :
      	_settextposition ( 3, 50 );
      	_outtext ( "Easy" );
			break;
    	case 2 :
      	_settextposition ( 3, 50 );
      	_outtext ( "Medium" );
			break;
    	case 3 :
      	_settextposition ( 3, 50 );
      	_outtext ( "Hard" );
			break;
		default :
			;
		}

		if ( selected < 1 )
			selected = 3;
		if ( selected > 3 )
			selected = 1;
		if ( selected != 2 )
		{
			_settextposition ( 3, 54 );
    		_outtext ( "  " );
		}
	}

	*Diff = selected;

   _clearscreen ( _GCLEARSCREEN );

	switch ( *Diff )
	{
	case 1 :
      *youCol = 40;
      *youRow = 24;
      *Shields = 6;
		break;
    case 2 :
      *youCol = 60;
      *youRow = 18;
      *Shields = 4;
		break;
    case 3 :
      *youCol = 40;
      *youRow = 12;
      *Shields = 2;
		break;
	 default :
	 	;
	}
}

void Track ( int i, struct Enemy *enemyStatus, int youRow, int youCol )
{
	if ( youRow < enemyStatus[i].Row )
	{
		if ( youCol < enemyStatus[i].Col )
	 	{
      	switch ( enemyStatus[i].Dir )
			{
         case 2 :
				enemyStatus[i].Dir = 3;
				break;
         case 4 :
				enemyStatus[i].Dir = 1;
				break;
			default :
				;
			}
    	}
		else
		{
   		switch ( enemyStatus[i].Dir )
			{
      	case 2 :
				enemyStatus[i].Dir = 4;
      	case 3 :
				enemyStatus[i].Dir = 1;
			default :
				;
   	   }
    	}
 	}
	else
	{
   	if ( youCol < enemyStatus[i].Col )
		{
   		switch ( enemyStatus[i].Dir )
			{
      	case 1 :
				enemyStatus[i].Dir = 3;
				break;
      	case 4 :
				enemyStatus[i].Dir = 2;
				break;
			default :
				;
     		}
   	}
		else
		{
    		switch ( enemyStatus[i].Dir )
			{
      	case 1 :
				enemyStatus[i].Dir = 4;
				break;
      	case 3 :
				enemyStatus[i].Dir = 2;
				break;
			default :
				;
      	}
		}
   }
}

int GetKey ( )
{
	int kbd;

	_settextcolor ( 9 );

  	_settextposition ( 1, 1 );
  	/*_outtext ( TIME$ */	

	if ( _kbhit ( ) )
		kbd = getch ( );
	else
		return ( NO_KEY );
		

	return ( kbd );
}

void Help ( )
{
	int kbd;
   _settextcolor ( 7 );
   _clearscreen ( _GCLEARSCREEN );
   _settextposition ( 3, 5 );
   _outtext ( "To move your ship use the up, down, left, and right arrows to move." );
   _settextposition ( 5, 25 );
   _outtext ( "To shoot, press the spacebar." );
   _settextposition ( 7, 20 );
   _outtext ( "To rotate your ship ust the T and H keys." );
   _settextposition ( 9, 25 );
   _outtext ( "To quit this game press Alt F4." );
	getch ( );
  _clearscreen ( _GCLEARSCREEN );
}

void Pause ( float delayNum )
{
	// Use a windows function now.
	Sleep(delayNum);

	/*
	double delay = 8.4678549097;
 	double i;
 
	for ( i = 1; i <= delayNum; i += 0.1 )
  		delay = delay + delay - delay;
	*/
}

void Shoot ()
{
	#define MAX_DIR 4

	int a;

	//const char *SHIP[4] = { "^", ">", "v", "<" };

	int Start [ MAX_DIR ] = { youRow, youCol, youRow, youCol};
	int End [ MAX_DIR ] = { 1, 80, 25, 2 };
	int *Row [ MAX_DIR ] = { &a, &youRow, &a, &youRow };
	int *Col [ MAX_DIR ] = { &youCol, &a, &youCol, &a };
	int Direction [ MAX_DIR ] = { -1, 1, 1, -1 };
	char *FireChar [ MAX_DIR ] = { "|", "-", "|", "-" };
	int b, count;
	int foundEnemiesInSpot = FALSE;

	_settextcolor ( 10 );

	for ( count = 1; count <= 29; count++ )
	{
		;
		/*SOUND 3000 - count * 100, .2*/
	}

   a = Start [ youFacing ];

   while ( *Row [ youFacing ] >= TOP_ROW_LIMIT && *Row [ youFacing ] <
		BOTTOM_ROW_LIMIT && *Col [ youFacing ] >= LEFT_COL_LIMIT &&
		*Col [ youFacing ] <= RIGHT_COL_LIMIT )
	{
     	_settextposition ( *Row [ youFacing ], *Col [ youFacing ] );
    	_outtext ( FireChar [ youFacing ] );
		for ( b = 1; b <= level * 5; b++ )
		{
			if ( enemyStatus[b].Row == *Row [ youFacing ] &&
				enemyStatus[b].Col == *Col [ youFacing ]  )
			{
				enemyStatus[b].Armor--;
				Score += 50;
				if ( enemyStatus[b].Armor <= 0 )
				{
					
					enemyStatus[b].Died = TRUE;
					enemiesAlive--;

           			/*SOUND 3500, .3*/
            		Score += 250;
				}
				foundEnemiesInSpot = TRUE;
			}
		}

		if (foundEnemiesInSpot) {
			break;
		}

		a += Direction [ youFacing ];
   }

   a = Start [ youFacing ];

   Sleep(200);

   while ( *Row [ youFacing ] >= TOP_ROW_LIMIT && *Row [ youFacing ] <
		BOTTOM_ROW_LIMIT && *Col [ youFacing ] >= LEFT_COL_LIMIT &&
		*Col [ youFacing ] <= RIGHT_COL_LIMIT )
	{
     	_settextposition ( *Row [ youFacing ], *Col [ youFacing ] );
    	_outtext ( " " );
		a += Direction [ youFacing ];
   }
}

void specialHelp1 ( )
{
	_clearscreen ( _GCLEARSCREEN );
   _settextcolor ( 7 );
   _settextposition ( 3, 5 );
   _outtext ( "Before you really start playing, I recomend you try out a few of" );
   _settextposition ( 4, 10 );
   _outtext ( "the speeds before you decide witch one you like best." );
   _settextposition ( 6, 25 );
   _outtext ( "The speed of your computer can" );
   _settextposition ( 7, 20 );
   _outtext ( "also effect the speed yhe speeds before you decide witch one you like best." );
   _settextposition ( 6, 25 );
   _outtext ( "The speed of your computer can" );
   _settextposition ( 7, 20 );
   _outtext ( "also effect the speed you should play at" );
	getch ( );
   _clearscreen ( _GCLEARSCREEN );
   _settextcolor ( 3 );
   _settextposition ( 1, 30 );
   _outtext ( "Battle by David" );
   _settextcolor ( 7 );
   _settextposition ( 3, 20 );
   _outtext ( "Type in a number between 0 and 9," );
   _settextposition ( 4, 17 );
   _outtext ( "for how fast do you want the game to go" );
   _settextcolor ( 9 );
}

void SpecialHelp2 ( )
{
    _settextcolor ( 7 );
    _settextposition ( 3, 15 );
    _outtext ( "On Easy mode you get 6 shields, and start on a corner." );
    _settextposition ( 5, 15 );
    _outtext ( "On Medium mode you get 4 shields, and start in betwee the corner and the middle." );
    _settextposition ( 8, 15 );
    _outtext ( "On Hard mode you get 2 shields, and start in the middle." );
    _settextposition ( 10, 20 );
    /*_outtext ( "NO*/
}

/* Sounds the speaker for a time specified in microseconds by duration
 * at a pitch specified in hertz by frequency.
 */
void Beep_Old( int frequency, int duration )
{	
    int control;

	// Sound isn't supported right now.
	return;

    /* If frequency is 0, Beep doesn't try to make a sound. It
     * just sleeps for the duration.
     */
    if( frequency )
    {
        /* 75 is about the shortest reliable duration of a sound. */
        if( duration < 75 )
            duration = 75;

        /* Prepare timer by sending 10111100 to port 43. */
        outp( 0x43, 0xb6 );

        /* Divide input frequency by timer ticks per second and
         * write (byte by byte) to timer.
         */
        frequency = (unsigned)(1193180L / frequency);
        outp( 0x42, (char)frequency );
        outp( 0x42, (char)(frequency >> 8) );

        /* Save speaker control byte. */
        control = inp( 0x61 );

        /* Turn on the speaker (with bits 0 and 1). */
        outp( 0x61, control | 0x3 );
    }

    Sleep( (clock_t)duration );

    /* Turn speaker back on if necessary. */
    if( frequency )
        outp( 0x61, control );

}

void Sleep_Old( clock_t wait )
{
    clock_t goal;

    goal = wait + clock();
    while( goal > clock() )
        ;
}

